/*
 * This program is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later
 * version.
 * 
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 * details.
 * 
 * You should have received a copy of the GNU General Public License along with
 * this program. If not, see <http://www.gnu.org/licenses/>.
 */
package daemonClient;

import java.awt.TextField;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;
import java.rmi.UnexpectedException;

import javax.swing.JDialog;
import javax.swing.JOptionPane;

import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;

public class DaemonClient
{
	private static String _ip = "127.0.0.1";
	private static int _port = 2107;
	private static Socket _socket;
	protected Shell shell;
	private PrintStream out = null;
	private BufferedReader in = null;;
	private Button connectButton, RestartButton, shutdownButton, disconnectButton,
			exitButton = null;
	private Text display = null;
	private TextField password = new TextField("");
	private JDialog dialog = null;
	private Reader reader = null;

	public static void main(String[] args)
	{

		try
		{
			DaemonClient window = new DaemonClient();
			window.open();
		}
		catch (Exception e)
		{
			e.printStackTrace();
		}

	}

	public void open()
	{
		Display display = Display.getDefault();
		createContents();
		shell.open();
		shell.layout();
		while (!shell.isDisposed())
		{
			if (!display.readAndDispatch())
			{
				display.sleep();
			}
		}
	}

	/**
	 * Create contents of the window.
	 */
	protected void createContents()
	{
		shell = new Shell(SWT.SHELL_TRIM & ~SWT.RESIZE & ~SWT.MAX);
		shell.setSize(450, 300);
		shell.setText("Daemon Client");

		connectButton = new Button(shell, SWT.NONE);
		connectButton.addSelectionListener(new SelectionAdapter()
		{
			@Override
			public void widgetSelected(SelectionEvent e)
			{
				try
				{
					_socket = new Socket(_ip, _port);
					out = new PrintStream(_socket.getOutputStream());
					in = new BufferedReader(new InputStreamReader(_socket.getInputStream()));
					dialog.toFront();
					dialog.requestFocus();
					dialog.setAlwaysOnTop(true);
					dialog.setVisible(true);
					if (Sifrovani.isPhraseSet())
						getPhrase(false);
					else
						getPhrase(true);
					reader = new Reader();
					reader.start();
					out.println(Sifrovani.encrypt("Password" + password.getText()));
					display.append("Connected and password sent.\n");
					setVisibilityForAllExceptConnectButton(true);
				}
				catch (Exception ee)
				{
					if (out != null)
						out.close();
					if (ee.getMessage().contains("Connection refused: connect"))
						display.append("Daemon server is not online.\n");
					else
						display.append(ee.getMessage() + "\n");
				}
			}
		});
		connectButton.setBounds(41, 172, 68, 23);
		connectButton.setText("Connect");

		RestartButton = new Button(shell, SWT.NONE);
		RestartButton.addSelectionListener(new SelectionAdapter()
		{
			@Override
			public void widgetSelected(SelectionEvent e)
			{
				try
				{
					out.println(Sifrovani.encrypt("SystemControlRestart"));
					display.append("Restart command sent.\n");
				}
				catch (UnexpectedException e1)
				{
					e1.printStackTrace();
				}
			}
		});
		RestartButton.setBounds(115, 172, 68, 23);
		RestartButton.setText("Restart");
		RestartButton.setEnabled(false);

		shutdownButton = new Button(shell, SWT.NONE);
		shutdownButton.addSelectionListener(new SelectionAdapter()
		{
			@Override
			public void widgetSelected(SelectionEvent e)
			{
				try
				{
					out.println(Sifrovani.encrypt("SystemControlShutdown"));
					display.append("Shutdown command sent.\n");
					disableAllButtonsExceptExit();
				}
				catch (UnexpectedException e1)
				{
					e1.printStackTrace();
				}
			}
		});
		shutdownButton.setBounds(189, 172, 68, 23);
		shutdownButton.setText("Shutdown");
		shutdownButton.setEnabled(false);

		disconnectButton = new Button(shell, SWT.NONE);
		disconnectButton.addSelectionListener(new SelectionAdapter()
		{
			@Override
			public void widgetSelected(SelectionEvent e)
			{
				try
				{
					out.println(Sifrovani.encrypt("DaemonControlShutdown"));
					_socket.close();
					out.close();
					setVisibilityForAllExceptConnectButton(false);
					reader.interrupt();
				}
				catch (Exception e1)
				{
				}
			}
		});
		disconnectButton.setBounds(263, 172, 68, 23);
		disconnectButton.setText("Disconnect");
		disconnectButton.setEnabled(false);

		exitButton = new Button(shell, SWT.NONE);
		exitButton.addSelectionListener(new SelectionAdapter()
		{
			@Override
			public void widgetSelected(SelectionEvent e)
			{
				try
				{
					_socket.close();
					out.close();
					System.exit(1);
				}
				catch (Exception e1)
				{
				}
			}
		});
		exitButton.setBounds(337, 172, 68, 23);
		exitButton.setText("Exit");
		exitButton.setEnabled(true);

		display = new Text(shell, SWT.WRAP | SWT.BORDER | SWT.VERTICAL);
		display.setBounds(1, 1, 442, 100);
		display.setEditable(false);

		JOptionPane p = new JOptionPane("");
		p.setBounds(shell.getBounds().x + 130, shell.getBounds().y + 50, 200, 200);
		p.add(password);
		dialog = p.createDialog("Enter password");
		dialog.setVisible(false);

	}

	private void disableAllButtonsExceptExit()
	{
		connectButton.setEnabled(false);
		RestartButton.setEnabled(false);
		shutdownButton.setEnabled(false);
		disconnectButton.setEnabled(false);
	}

	private void setVisibilityForAllExceptConnectButton(boolean b)
	{
		connectButton.setEnabled(!b);
		RestartButton.setEnabled(b);
		shutdownButton.setEnabled(b);
		disconnectButton.setEnabled(b);
	}

	public void shutdown()
	{
		try
		{
			_socket.close();
			out.close();
			shell.close();
		}
		catch (IOException e)
		{
		}
	}

	private void getPhrase(boolean b)
	{
		try
		{
			BufferedOutputStream bout = new BufferedOutputStream(_socket.getOutputStream());
			BufferedInputStream bin = new BufferedInputStream(_socket.getInputStream());

			if (b)
			{
				writeData("true".getBytes(), bout);
				Thread.sleep(100);
				// 1st pass
				String data = readData(bin);
				Thread.sleep(100);
				Vernam v = new Vernam();
				v.setPhrase(data);
				v.generateKey();
				writeData(v.XOR(), bout);
				Thread.sleep(100);
				// 2nd pass
				String s = readData(bin);
				Thread.sleep(100);
				v.setPhrase(s);
				Sifrovani.setPhrase(new String(v.XOR()));
				v.forget();
			}
			else 
				writeData("false".getBytes(), bout);
		}
		catch (Exception e)
		{
			e.printStackTrace();
		}
	}

	private void writeData(byte[] xor, BufferedOutputStream bout) throws IOException
	{
		int len = xor.length + 2;
		bout.write(len & 0xff);
		bout.write(len >> 8 & 0xff);
		bout.write(xor);
		bout.flush();

	}

	private String readData(BufferedInputStream bin) throws IOException
	{
		int lengthLo = bin.read();
		int lengthHi = bin.read();
		int length = lengthHi * 256 + lengthLo;

		byte[] data = new byte[length - 2];

		int receivedBytes = 0;
		int newBytes = 0;
		int left = length - 2;
		while (newBytes != -1 && receivedBytes < length - 2)
		{
			newBytes = bin.read(data, receivedBytes, left);
			receivedBytes = receivedBytes + newBytes;
			left -= newBytes;
		}

		return new String(data);
	}

	class Reader extends Thread
	{
		@Override
		public void run()
		{
			while (true)
			{
				try
				{
					final String msg = Sifrovani.decrypt(in.readLine());

					if (msg == null)
						continue;
					System.out.println(msg);
					if (msg.contains("DaemonLoginFail"))
					{
						Display.getDefault().asyncExec(new Runnable()
						{
							@Override
							public void run()
							{
								try
								{
									display.append("Bad password.\n");
									_socket.close();
									in.close();
									out.close();
									setVisibilityForAllExceptConnectButton(false);
									interrupt();
								}
								catch (Exception ee)
								{
								}
							}
						});
					}
					Thread.sleep(100);
				}
				catch (IOException e)
				{
					this.interrupt();
				}
				catch (InterruptedException e)
				{
				}
			}
		}
	}
}
